package com.javabasic.net.netty_learning;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;

/**
 * @author xiongmin
 * @version 1.0
 * @since Created by work on 2021-10-12 15:40
 * @description Netty4.X版本
 */
public class NettyServerDemo1 {

    public static void main(String[] args) {
        /**
         * Configure the Server
         * 1. 创建两个EventLoopGroup -- boss 和 worker的线程模型
         *
         * 要启动Netty服务端,必须要指定三类属性，分别是线程模型、IO模型、连接读写处理逻辑
         * Netty服务端启动的流程是创建引导类给引导类指定线程模型、IO模型、连接读写处理逻辑、绑定端口之后服务端就启动起来了
         * bind 方法是异步的通过异步机制来实现端口递增绑定
         * Netty服务端启动额外的参数，主要包括给服务端channel或者属性值，设置底层TCP参数
         *
         * Netty服务端启动流程: 创建引导类 -> 指定线程模型、IO模型、连接读写处理逻辑 -> 绑定端口
         */

        // 创建boss线程组 用于接收服务端客户端连接
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);

        // 创建worker线程组 用于进行SocketChannel的数据读写
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();

        ServerBootstrap serverBootstrap = new ServerBootstrap();

        ChannelFuture channelFuture = serverBootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .handler(new LoggingHandler(LogLevel.INFO))
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        socketChannel.pipeline().addLast(new StringDecoder());
                        socketChannel.pipeline().addLast(new SimpleChannelInboundHandler<String>() {

                            /*
                             * 每当从服务端读到客户端写入信息时，
                             * 其中如果你使用的是 Netty 5.x 版本时，
                             * 需要把 channelRead0() 重命名为messageReceived()
                             */
                            @Override
                            protected void channelRead0(ChannelHandlerContext channelHandlerContext, String msg) throws Exception {
                                System.out.println(msg);
                            }

                            /*
                             * 方法在channel被启用的时候触发（在建立连接的时候）
                             * 服务端监听到客户端活动
                             */
                            @Override
                            public void channelActive(ChannelHandlerContext ctx) throws Exception {
                                super.channelActive(ctx);

                                System.out.println("--------在channel被启用的时候触发（在建立连接的时候）--------");

                            }

                            /*
                             * exceptionCaught() 事件处理方法是当出现 Throwable 对象才会被调用，
                             * 即当 Netty 由于 IO 错误或者处理器在处理事件时抛出的异常时。
                             * 在大部分情况下，捕获的异常应该被记录下来并且把关联的 channel 给关闭掉。
                             * 然而这个方法的处理方式会在遇到不同异常的情况下有不同的实现，
                             * 比如你可能想在关闭连接之前发送一个错误码的响应消息。
                             */
                            @Override
                            public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                                super.exceptionCaught(ctx, cause);

                                System.out.println("--------客户端连接异常--------");
                            }

                            /**
                             * 每当从服务端收到新的客户端连接时触发
                             * @param ctx
                             * @throws Exception
                             */
                            @Override
                            public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
                                super.handlerAdded(ctx);

                                System.out.println("--------有新的客户端连接过来--------");
                            }

                            /**
                             * 每当从服务端收到客户端断开时触发
                             * @param ctx
                             * @throws Exception
                             */
                            @Override
                            public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
                                super.handlerRemoved(ctx);

                                System.out.println("--------客户端连接断开--------");
                            }


                        });
                    }
                })
                .bind(8000)
                .addListener(new GenericFutureListener<Future<? super Void>>() {
                    @Override
                    public void operationComplete(Future<? super Void> future) throws Exception {
                        if (future.isSuccess()) {
                            System.out.println("端口[" + 8000 + "]绑定成功");
                        } else {
                            System.out.println("端口[" + 8000 + "]绑定失败");
                        }
                    }
                });


        // 监听服务端关闭，并阻塞等待
        try {
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
            System.out.println("-----服务端关闭-------");
        }



    }
}
